/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufpr.inf.escel.testing;

import br.ufpr.inf.escel.consts.Constantes;
import br.ufpr.inf.escel.manager.util.Arquivo;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.channels.FileChannel;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author gustavo
 */
public class Console extends Thread {

    private static Console instance;
    private String novoDirCut;
    private int value = 1;

    private Console() {
    }

    public static Console getInstance() {
        if (instance == null) {
            instance = new Console();
        }
        return instance;
    }

    /**
     * Metodo que faz a comunicação com o terminal. O comando é passado por
     * parametro, é executado, e o resultado dele é retornado.
     * @param command
     * @return o resultado do comando solicitado
     * @since 4.4
     */
    public synchronized String run(String... comandos) {
        acquire();

        StringBuffer out = new StringBuffer();
        File sh = Arquivo.escreva(Constantes.PROJECT_NAME + ".sh",
                new File(".").toString(), comandos);

        String cmd[] = {"chmod", "755", sh.toString()};

        try {
            Runtime.getRuntime().exec(cmd);
            ProcessBuilder pBuilder = new ProcessBuilder(sh.toString());

            StringBuffer dirFerramentas = new StringBuffer(":");
            dirFerramentas.append(Constantes.PASTA_POKE + "/bin:");
            dirFerramentas.append(Constantes.PASTA_PROTEUM);

            Map<String, String> env1 = pBuilder.environment();
            env1.put("PATH", env1.get("PATH") + dirFerramentas.toString());

            env1.put("PROTEUM14HOME", Constantes.PASTA_PROTEUM);
            env1.put("POKEBIN", Constantes.PASTA_POKE + "/bin");
            env1.put("POKETABS", Constantes.PASTA_POKE + "/bin/poketabs");
            env1.put("LITABS", Constantes.PASTA_POKE + "/bin/litabs");

            pBuilder.redirectErrorStream(true);

            String line = null;
            Process proc = null;
            BufferedReader reader = null;

            try {

                proc = pBuilder.start();
                proc.waitFor();
                reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));

                while ((line = reader.readLine()) != null) {
                    out.append(line).append("\n");
                }

            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                if (proc != null) {
                    close(proc.getErrorStream());
                    close(proc.getInputStream());
                    close(proc.getOutputStream());
                    proc.destroy();
                    reader.close();
                    sh = null;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        release();
        return out.toString();
    }

    /**
     * Cria uma pasta com o nome da cut. Posteriormente, as
     * saidas são escritas nessa pasta
     * @param cut
     * @return endereço da nova pasta criada
     */
    private String createDir(String cut) {
        File atualDir = new File(".");
        File novoDir = null;
        try {
            String diretorioCorrente = atualDir.getCanonicalPath();
            novoDir = new File(diretorioCorrente + "/" + cut);
            if (!novoDir.exists()) {
                novoDir.mkdir();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return novoDir.toString();
    }

    /**
     * Faz a copia do arquivo no seu local de origem, para o novo
     * local criado
     * @param cut
     * @param oldPath
     * @param novoPath
     */
    public void copy(String cut, String oldPath, String novoPath) {
        // Cria channel na origem
        FileChannel oriChannel = null;
        FileChannel destChannel = null;
        try {
            oriChannel = new FileInputStream(oldPath + "/" + cut).getChannel();
            // Cria channel no destino
            destChannel = new FileOutputStream(novoPath + "/" + cut).getChannel();
            // Copia conteúdo da origem no destino
            destChannel.transferFrom(oriChannel, 0, oriChannel.size());
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                oriChannel.close();
                destChannel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Sintetiza os dois metodos para criar pasta e copiar arquivo
     * @param cut
     * @param dirAtualCut
     * @return diretorio do novo arquivo criado
     */
    public String createCopyCut(String cut, String dirAtualCut) {
        setNovoDirCut(createDir(cut));
        copy(cut, dirAtualCut, novoDirCut);
        return novoDirCut;
    }

    public void setNovoDirCut(String novoDirCut) {
        this.novoDirCut = novoDirCut;
    }

    public synchronized void acquire() {
        while (value == 0) {
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Console.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        value--;
    }

    public synchronized void release() {
        ++value;
        notify();
    }

    private static void close(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException ex) {
                Logger.getLogger(Console.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
